package fag.core.geometry;

import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;

import fag.core.collision.Hitbox;

/**
 * Classe de base des polygones.
 * Implémente l'algorithme de Hill Climbing pour le support mapping.
 * @author lumz
 */
public abstract class AbstractPolygon extends Hitbox {

	private static final long serialVersionUID = 8954440746326366262L;

	/**
	 * Accesseur des sommets du polygone, le dernier étant connecté au premier.
	 * @return Les sommets du polygone.
	 */
	public abstract List<Vector2D> getVertices();
	
	/**
	 * Calcul du vecteur d'assistance pour les algorithmes GJK et EPA.
	 * Implémentation de l'algorithme Hill Climbing.
	 */
	public Vector2D supportMapping(Vector2D d) {
		
		List<Vector2D> vertices = getVertices();
		int size = vertices.size();
		
		// Gestion des cas où le nombre de sommets est inférieur à 2
		switch(size) {
		case 0:
			return null;
		case 1:
			return vertices.get(0);
		}
		
		// Trouver d'où et dans quel sens grimper
		int maxIndex = 0;
		int nextIndex = 1;
		Winding winding;
		
		double max = d.dot(vertices.get(maxIndex));
		double next = d.dot(vertices.get(nextIndex));
		
		if(max < next)
		{
			winding = Winding.COUNTERCLOCKWISE;
			maxIndex = nextIndex;
			nextIndex = 2;
			max = next;
		}
		else
		{
			winding = Winding.CLOCKWISE;
			nextIndex = size - 1;
		}
		
		// Grimper
		while(true) {
			
			next = d.dot(vertices.get(nextIndex));
			
			// Si le sommet suivant est moins éloigné alors terminer
			if(next < max)
				return vertices.get(maxIndex);
			
			max = next;
			maxIndex = nextIndex;
			
			switch(winding) {
			case CLOCKWISE :
				nextIndex = (nextIndex - 1) < 0 ? size - 1 : nextIndex - 1;
				break;
				
			case COUNTERCLOCKWISE : 
				nextIndex = (nextIndex + 1) % size;
				break;
			}
		}
	}
	
	@Override
	public Polygon2D transform(AffineTransform transformation) {

		List<Vector2D> vertices = getVertices();
		List<Vector2D> result = new ArrayList<Vector2D>();
		
		for(Vector2D vertex : vertices) {
			result.add(vertex.transform(transformation));
		}
		
		return new Polygon2D(result);
	}
}
